/**
 * 
 */
package idv.takeshi.software.productline.bookstore.infrastructure.persistence.jpa;

import java.util.Set;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import idv.takeshi.software.productline.bookstore.domain.model.category.Category;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryDuplicateException;
import idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository;

import org.apache.commons.lang.Validate;
import org.springframework.stereotype.Repository;

/**
 * A model repository implementation for CategoryRepository.
 * @author takeshi
 *
 */
@Repository("categoryRepository")
public class CategoryRepositoryImpl implements CategoryRepository {
	
	@PersistenceContext
	private EntityManager entityManager;
	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository#add(idv.takeshi.software.productline.bookstore.domain.model.category.Category)
	 */
	public void add(Category category) throws CategoryDuplicateException {
		Validate.notNull(category, "category shall always not be null");
		try {
		this.entityManager.persist(category);
		} catch(EntityExistsException  e){
			throw new CategoryDuplicateException("category:" + category.getName() + " is duplicate");
		}
	}

	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository#findById(java.lang.Long)
	 */
	public Category findById(Long id) {
		Validate.notNull(id, "id shall always not be null");
		return this.entityManager.find(Category.class, id);
	}

	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository#findRoot()
	 */
	public Category findRoot() {
		Query query = this.entityManager.createNamedQuery("findByRoot");
		Category category = null;
		try {
			category = (Category) query.getSingleResult();
		} catch(NoResultException e){
			//DO NOTHING
		}
		return category;
	}

	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository#remove(idv.takeshi.software.productline.bookstore.domain.model.category.Category)
	 */
	public void remove(Category category) {
		Validate.notNull(category, "category shall always not be null");
		this.entityManager.remove(category);
	}
	
	/* (non-Javadoc)
	 * @see idv.takeshi.software.productline.bookstore.domain.model.category.CategoryRepository#update(idv.takeshi.software.productline.bookstore.domain.model.category.Category)
	 */
	public void update(Category category){
		Validate.notNull(category, "category shall always not be null");
		this.entityManager.merge(category);
		if(category.hasSubCategories())
			this.updateCategories(category.getSubCategories());
	}
	
	/**
	 * Update sub-categories recursively.
	 * @param categories
	 */
	private void updateCategories(Set<Category> categories){
		for(Category category : categories){
			this.entityManager.merge(category);
			if(category.hasSubCategories())
				updateCategories(category.getSubCategories());
		}
	}

}
